package com.ebupt.migu.music.utils;

import com.jcraft.jsch.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.*;
import java.util.Arrays;

/**
 * @description: TODO
 * @author: liuyangyang@ebupt.com
 * @date: 2021/5/24 15:13
 */
@Slf4j
@Component
public class SFTPUtils {
    @Value("${sftp.client.protocol}")
    private String SFTP_CLIENT_PORTOCOL;
    @Value("${sftp.client.host}")
    private String SFTP_CLIENT_HOST;
    @Value("${sftp.client.port}")
    private Integer SFTP_CLIENT_PORT;
    @Value("${sftp.client.username}")
    private String SFTP_CLIENT_USERNAME;
    @Value("${sftp.client.password}")
    private String SFTP_CLIENT_PASSWORD;
    @Value("${sftp.client.root}")
    private String SFTP_CLIENT_ROOT;
    @Value("${sftp.client.sessionStrictHostKeyChecking}")
    private String SFTP_CLIENT_KEY;
    @Value("${sftp.client.sessionConnectTimeout}")
    private int SFTP_CLIENT_SESSION;
    @Value("${sftp.client.channelConnectedTimeout}")
    private int SFTP_CLIENT_CHANNEL;


    // 设置第一次登陆的时候提示，可选值：(ask | yes | no)
    private static final String SESSION_CONFIG_STRICT_HOST_KEY_CHECKING = "StrictHostKeyChecking";

    /**
     * 创建SFTP连接
     *
     * @return
     * @throws Exception
     */
    private ChannelSftp createSftp() throws Exception {
        JSch jsch = new JSch();
        log.info("创建sftp链接：[" + SFTP_CLIENT_USERNAME + "@" + SFTP_CLIENT_HOST + "]");
        Session session = createSession(jsch, SFTP_CLIENT_HOST, SFTP_CLIENT_USERNAME, SFTP_CLIENT_PORT);
        session.setPassword(SFTP_CLIENT_PASSWORD);
        session.connect(SFTP_CLIENT_SESSION);
        Channel channel = session.openChannel(SFTP_CLIENT_PORTOCOL);
        channel.connect(SFTP_CLIENT_CHANNEL);
        return (ChannelSftp) channel;
    }


    /**
     * 上传文件
     *
     * @param inputStream
     * @return
     * @throws Exception
     */
    public boolean uploadFile(String fileName, InputStream inputStream) throws Exception {
        ChannelSftp sftp = this.createSftp();
        try {
            sftp.cd(SFTP_CLIENT_ROOT);
            log.info("切换路径至：{}", SFTP_CLIENT_ROOT);
            sftp.put(inputStream, fileName);
            return true;
        } catch (Exception e) {
            log.error("文件上传失败，目标文件：{}", fileName, e);
            throw new Exception("文件上传失败");
        } finally {
            this.disconnect(sftp);
        }
    }

    /**
     * 上传文件
     *
     * @param file
     * @return
     * @throws Exception
     */
    public boolean uploadFile(String fileName, File file) throws Exception {
        return this.uploadFile(fileName, new FileInputStream(file));
    }

    /**
     * 下载文件
     *
     * @param targetPath
     * @return
     * @throws Exception
     */
    public File downloadFile(String targetPath) throws Exception {
        ChannelSftp sftp = this.createSftp();
        OutputStream outputStream = null;
        try {
            sftp.cd(SFTP_CLIENT_ROOT);
            log.info("Change path to {}", SFTP_CLIENT_ROOT);

            File file = new File(targetPath.substring(targetPath.lastIndexOf("/") + 1));

            outputStream = new FileOutputStream(file);
            sftp.get(targetPath, outputStream);
            log.info("Download file success. TargetPath: {}", targetPath);
            return file;
        } catch (Exception e) {
            log.error("Download file failure. TargetPath: {}", targetPath, e);
            throw new Exception("Download File failure");
        } finally {
            if (outputStream != null) {
                outputStream.close();
            }
            this.disconnect(sftp);
        }
    }

    /**
     * 删除文件
     *
     * @param targetPath
     * @return
     * @throws Exception
     */
    public boolean deleteFile(String targetPath) throws Exception {
        ChannelSftp sftp = null;
        try {
            sftp = this.createSftp();
            sftp.cd(SFTP_CLIENT_ROOT);
            sftp.rm(targetPath);
            return true;
        } catch (Exception e) {
            log.error("Delete file failure. TargetPath: {}", targetPath, e);
            throw new Exception("Delete File failure");
        } finally {
            this.disconnect(sftp);
        }
    }

    /**
     * 创建一级或者多级目录
     *
     * @param dirPath
     * @param sftp
     * @return
     */
    private boolean createDirs(String dirPath, ChannelSftp sftp) {
        if (dirPath != null && !dirPath.isEmpty()
                && sftp != null) {
            String[] dirs = Arrays.stream(dirPath.split("/"))
                    .filter(StringUtils::isNotBlank)
                    .toArray(String[]::new);

            for (String dir : dirs) {
                try {
                    sftp.cd(dir);
                    log.info("Change directory {}", dir);
                } catch (Exception e) {
                    try {
                        sftp.mkdir(dir);
                        log.info("Create directory {}", dir);
                    } catch (SftpException e1) {
                        log.error("Create directory failure, directory:{}", dir, e1);
                        e1.printStackTrace();
                    }
                    try {
                        sftp.cd(dir);
                        log.info("Change directory {}", dir);
                    } catch (SftpException e1) {
                        log.error("Change directory failure, directory:{}", dir, e1);
                        e1.printStackTrace();
                    }
                }
            }
            return true;
        }
        return false;
    }

    /**
     * 创建session
     *
     * @param jsch
     * @param host
     * @param username
     * @param port
     * @return
     * @throws Exception
     */
    private Session createSession(JSch jsch, String host, String username, Integer port) throws Exception {
        Session session = null;

        if (port <= 0) {
            session = jsch.getSession(username, host);
        } else {
            session = jsch.getSession(username, host, port);
        }

        if (session == null) {
            throw new Exception(host + " session is null");
        }

        session.setConfig(SESSION_CONFIG_STRICT_HOST_KEY_CHECKING, SFTP_CLIENT_KEY);
        return session;
    }

    /**
     * 关闭连接
     *
     * @param sftp
     */
    private void disconnect(ChannelSftp sftp) {
        try {
            if (sftp != null) {
                if (sftp.isConnected()) {
                    sftp.disconnect();
                } else if (sftp.isClosed()) {
                    log.info("sftp is closed already");
                }
                if (null != sftp.getSession()) {
                    sftp.getSession().disconnect();
                }
            }
        } catch (JSchException e) {
            e.printStackTrace();
        }
    }
}
